package cube

import (
	"bytes"
	"database/sql"
	"fmt"
	"shqsoft.cn/pasnj/base"
	"shqsoft.cn/pasnj/bi/database"
	"shqsoft.cn/pasnj/bi/datapackage"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis"
	log "github.com/sirupsen/logrus"
)

//Cube 度量值
type Cube struct {
	redis            *redis.Client
	setting          *datapackage.Cube
	hkey             string
	lkey             string
	skey             string
	agghKeys         []string
	agglKeys         []string
	aggsKeys         []string
	aggtmpKeys       []string
	aggRedis         []*redis.Client
	timeDimIndex     int
	aggTimeDimIndexs []int
	currIndex        int
}

//newCube 创建新的Cube对象
func newCube(cube *datapackage.Cube) (*Cube, error) {
	aLen := len(cube.Aggregates)
	agghKeys := make([]string, aLen, aLen)
	agglKeys := make([]string, aLen, aLen)
	aggsKeys := make([]string, aLen, aLen)
	aggtmpKeys := make([]string, aLen, aLen)
	aggRedis := make([]*redis.Client, aLen, aLen)
	timeDimIndex := -1
	for i, dim := range cube.DimensionUsages {
		if dim.Name == timeDimName {
			timeDimIndex = i
		}
	}
	if timeDimIndex == -1 {
		return nil, fmt.Errorf("没有为CUBE:%s指定时间维度！", cube.Name)
	}
	aggTimeDimIndexs := make([]int, aLen, aLen)
	for i, agg := range cube.Aggregates {
		agghKeys[i] = fmt.Sprintf("%s:%s:h:", cube.Name, agg.Name)
		agglKeys[i] = fmt.Sprintf("%s:%s:l:", cube.Name, agg.Name)
		aggsKeys[i] = fmt.Sprintf("%s:%s:s:", cube.Name, agg.Name)
		aggtmpKeys[i] = fmt.Sprintf("%s:%s:t:", cube.Name, agg.Name)
		aggRedis[i] = base.RedisClients[agg.RedisIndex]
		for j, aggdim := range agg.Dimensions {
			if aggdim == timeDimName {
				aggTimeDimIndexs[i] = j
			}
		}
	}
	return &Cube{
		redis:            base.RedisClients[cube.RedisIndex],
		setting:          cube,
		hkey:             fmt.Sprintf("%s:h:", cube.Name),
		skey:             fmt.Sprintf("%s:s:", cube.Name),
		lkey:             fmt.Sprintf("%s:l:", cube.Name),
		agghKeys:         agghKeys,
		agglKeys:         agglKeys,
		aggsKeys:         aggsKeys,
		aggtmpKeys:       aggtmpKeys,
		aggRedis:         aggRedis,
		timeDimIndex:     timeDimIndex,
		aggTimeDimIndexs: aggTimeDimIndexs,
	}, nil
}
func (c *Cube) build(start, end *time.Time) (err error) {
	ms := start.Format("20060102")
	me := end.Format("20060102")
	if err := c.delete(end); err != nil {
		return err
	}
	if c.setting.BuildPeriod == "month" {
		prevMe := end.AddDate(0, 0, -1)
		if err := c.delete(&prevMe); err != nil {
			return err
		}
	}
	log.Infof("开始构建事实表:%s,日期从:%s到%s....", c.setting.Table.Name, ms, me)
	var divi *Division
	for _, dim := range c.setting.DimensionUsages {
		if dim.Division != "" {
			divi, err = NewDivision(dim.Division)
			if err != nil {
				return err
			}
			break
		}
	}
	var rows *sql.Rows
	dl := database.DefaultDataLoader
	if c.setting.Table.Type == "proc" {
		rows, err = dl.LoadDataByProc(c.setting.Table.Name, start, end)
	} else {
		sqlStr := fmt.Sprintf("select * from %s where dtid>=? and dtid<=?", c.setting.Table.Name)
		rows, err = dl.LoadDataBySQL(sqlStr, ms, me)
	}
	if err != nil {
		return fmt.Errorf("从数据库%s：%s加载数据出错！%s", c.setting.Table.Type, c.setting.Table.Name, err)
	}
	cols, err := base.GetColumns(rows)
	if err != nil {
		return err
	}
	clen := len(cols)
	values := make([]sql.NullString, clen)
	scanArgs := make([]interface{}, clen)
	for i := 0; i < clen; i++ {
		scanArgs[i] = &values[i]
	}
	for rows.Next() {
		err := rows.Scan(scanArgs...)
		if err != nil {
			return err
		}
		if err := c.addData(base.ToMap(cols, values), divi); err != nil {
			return err
		}
		c.currIndex++
	}
	rows.Close()
	log.Infof("构建事实表:%s,日期从:%s到%s完成！", c.setting.Table.Name, ms, me)
	return
}

//AddData 向Cube中添加数据
func (c *Cube) addData(datas map[string]interface{}, divi *Division) error {
	var hkey2 string
	pkey := c.setting.Table.PrimaryKey
	if pkey != "" {
		val, ok := datas[pkey]
		if !ok {
			return fmt.Errorf("配置错误，主键名：%s在表%s中不存在！", pkey, c.setting.Table.Name)
		}
		hkey2 = base.ToString(val)
	} else {
		hkey2 = base.ToString(c.currIndex)
	}
	aLen := len(c.setting.Aggregates)
	dimLen := len(c.setting.DimensionUsages)
	dimKeys := make([]string, dimLen, dimLen)
	dimValMap := make(map[string]interface{})
	var timeDimVal, month string
	var diviDim string
	for i, dim := range c.setting.DimensionUsages {
		if dim.Division != "" {
			diviDim = dim.Name
		}
		if dimVal, ok := datas[dim.ForeignKey]; ok {
			if dimVal == nil {
				dimVal = "0"
			}
			dimValMap[dim.Name] = dimVal
			dimKeys[i] = base.ToString(dimVal)
			if c.timeDimIndex == i {
				timeDimVal = dimKeys[i]
				if timeDimVal == "" {
					return fmt.Errorf("CUBE:%s中键值%s的记录中时间维度存在空值", c.setting.Name, hkey2)
				}
				month = string(([]byte(timeDimVal))[:6])
			}
			if c.timeDimIndex != i && dim.ForeignKey != pkey && dim.Relation == "" {
				if err := c.redis.SAdd(c.skey+month+":d:"+strconv.Itoa(i)+":"+dimKeys[i], hkey2).Err(); err != nil {
					return err
				}
			}
		} else {
			return fmt.Errorf("配置错误，字段名：%s在表%s中不存在！", dim.ForeignKey, c.setting.Table.Name)
		}
	}
	aggDimKeys := make([][]string, aLen, aLen)
	diviDimIndexs := make([]int, aLen, aLen)
	for i, agg := range c.setting.Aggregates {
		adimLen := len(agg.Dimensions)
		aggDimKeys[i] = make([]string, adimLen, adimLen)
		diviDimIndexs[i] = -1
		for j, aggdim := range agg.Dimensions {
			if aggdim == diviDim {
				diviDimIndexs[i] = j
			}
			if dimVal, ok := dimValMap[aggdim]; ok {
				aggDimKeys[i][j] = base.ToString(dimVal)
			} else {
				return fmt.Errorf("配置错误，聚合：%s中的维度：%s不存在！", agg.Name, aggdim)
			}
		}
	}
	if c.setting.BuildPeriod == "day" {
		month := string(([]byte(timeDimVal))[:6])
		day := string(([]byte(timeDimVal))[6:])
		day = strings.TrimLeft(day, "0")
		for i, mea := range c.setting.Measures {
			hkey := c.hkey + month + ":" + hkey2 + ":"
			lkey := c.lkey + month
			hkey = hkey + strconv.Itoa(i)
			find := c.redis.Exists(hkey).Val()
			if find == 0 {
				if err := c.redis.RPush(lkey, hkey2+":"+strconv.Itoa(i)).Err(); err != nil {
					return fmt.Errorf("构建CUBE时，调用redis命令RPush出错:%s", err)
				}
			}
			v, ok := datas[mea.Column]
			if !ok {
				return fmt.Errorf("配置错误，字段名：%s在表%s中不存在！", mea.Column, c.setting.Table.Name)
			}
			if v == nil {
				continue
			}
			if err := c.redis.HSet(hkey, day, v).Err(); err != nil {
				return fmt.Errorf("构建CUBE时，调用redis命令HMSet出错:%s", err)
			}
		}
		for i := range c.setting.Aggregates {
			dimTimeKey := aggDimKeys[i][c.aggTimeDimIndexs[i]]
			month := string(([]byte(dimTimeKey))[:6])
			day := string(([]byte(dimTimeKey))[6:])
			day = strings.TrimLeft(day, "0")
			diviInfo := make(map[string]float64)
			diviIndex := diviDimIndexs[i]
			if diviDimIndexs[i] != -1 {
				diviKey := aggDimKeys[i][diviIndex]
				var err error
				diviInfo, err = divi.GetDivision(month, diviKey)
				if err != nil {
					return err
				}
				if len(diviInfo) == 0 {
					return fmt.Errorf("构建CUBE聚合时，从分户表%s中没有找到键值%s的占比", divi.getTableName(), diviKey)
				}
			} else {
				diviInfo["all"] = 1
			}
			dimNoTimeKeys := make([]string, 0)
			dimNoTimeKeys = append(dimNoTimeKeys, aggDimKeys[i][:c.aggTimeDimIndexs[i]]...)
			dimNoTimeKeys = append(dimNoTimeKeys, aggDimKeys[i][c.aggTimeDimIndexs[i]+1:]...)
			if diviIndex >= c.aggTimeDimIndexs[i] {
				diviIndex--
			}
			aggIndex := 0
			var agghkeyIndex string
			for k, divi := range diviInfo {
				if k != "all" {
					dimNoTimeKeys[diviIndex] = k
				}
				agglkey := c.agglKeys[i] + month
				agghkey := c.agghKeys[i] + month + ":"
				aggskey := c.aggsKeys[i] + month + ":"
				aggtmpkey := c.aggtmpKeys[i] + month
				dntkeys := strings.Join(dimNoTimeKeys, "-")
				exist, err := c.aggRedis[i].HExists(aggtmpkey, dntkeys).Result()
				if err != nil {
					return fmt.Errorf("构建CUBE聚集时，调用redis命令HExists出错:%s", err)
				}
				if !exist {
					aggIndex++
					if err := c.aggRedis[i].HSet(aggtmpkey, dntkeys, aggIndex).Err(); err != nil {
						return fmt.Errorf("构建CUBE聚集时，调用redis命令HSet出错:%s", err)
					}
					if err := c.aggRedis[i].RPush(agglkey, aggIndex).Err(); err != nil {
						return fmt.Errorf("构建CUBE聚集时，调用redis命令RPush出错:%s", err)
					}
					agghkeyIndex = strconv.Itoa(aggIndex)
					for x, dimKey := range aggDimKeys[i] {
						if x == c.aggTimeDimIndexs[i] {
							continue
						}
						if err := c.aggRedis[i].SAdd(aggskey+"d:"+strconv.Itoa(x)+":"+dimKey, agghkeyIndex).Err(); err != nil {
							return fmt.Errorf("构建CUBE聚集时，调用redis命令SAdd出错:%s", err)
						}
					}
				} else {
					agghkeyIndex, err = c.aggRedis[i].HGet(aggtmpkey, dntkeys).Result()
					if err != nil {
						return fmt.Errorf("构建CUBE聚集时，调用redis命令HGet出错:%s", err)
					}
				}
				for j, mea := range c.setting.Measures {
					v, ok := datas[mea.Column]
					if !ok {
						return fmt.Errorf("配置错误，字段名：%s在表%s中不存在！", mea.Column, c.setting.Table.Name)
					}
					if v == nil {
						continue
					}
					fv, err := strconv.ParseFloat(base.ToString(v), 64)
					if err != nil {
						return fmt.Errorf("装载Cube时出错， 字段：%s对应的值：%s不是数字！", mea.Column, v)
					}
					if err := c.aggRedis[i].HIncrByFloat(agghkey+agghkeyIndex+":"+strconv.Itoa(j), day, fv*divi).Err(); err != nil {
						return err
					}
				}
			}
		}
	} else {
		measures := make(map[string]interface{})
		for _, mea := range c.setting.Measures {
			v, ok := datas[mea.Column]
			if !ok {
				return fmt.Errorf("配置错误，字段名：%s在表%s中不存在！", mea.Column, c.setting.Table.Name)
			}
			if ok && v != nil {
				measures[mea.Name] = datas[mea.Column]
			}
		}
		if len(measures) == 0 {
			return nil
		}
		if err := c.redis.RPush(c.lkey+month, hkey2).Err(); err != nil {
			return fmt.Errorf("构建CUBE时，调用redis命令RPush出错:%s", err)
		}
		if err := c.redis.HMSet(c.hkey+month+":"+hkey2, measures).Err(); err != nil {
			return fmt.Errorf("构建CUBE时，调用redis命令HMSet出错:%s", err)
		}
		aggIndex := 0
		var agghkeyIndex string
		for i := range c.setting.Aggregates {
			dimNoTimeKeys := make([]string, 0)
			dimNoTimeKeys = append(dimNoTimeKeys, aggDimKeys[i][:c.aggTimeDimIndexs[i]]...)
			dimNoTimeKeys = append(dimNoTimeKeys, aggDimKeys[i][c.aggTimeDimIndexs[i]+1:]...)
			agghkey := c.agghKeys[i] + month
			agglkey := c.agglKeys[i] + month
			aggskey := c.aggsKeys[i] + month + ":"
			aggtmpkey := c.aggtmpKeys[i] + month
			dntkeys := strings.Join(dimNoTimeKeys, "-")
			exist, err := c.aggRedis[i].HExists(aggtmpkey, dntkeys).Result()
			if err != nil {
				return fmt.Errorf("构建CUBE聚集时，调用redis命令HExists出错:%s", err)
			}
			if !exist {
				aggIndex++
				if err := c.aggRedis[i].HSet(aggtmpkey, dntkeys, aggIndex).Err(); err != nil {
					return fmt.Errorf("构建CUBE聚集时，调用redis命令HSet出错:%s", err)
				}
				if err := c.aggRedis[i].RPush(agglkey, aggIndex).Err(); err != nil {
					return fmt.Errorf("构建CUBE聚集时，调用redis命令RPush出错:%s", err)
				}
				agghkeyIndex = strconv.Itoa(aggIndex)
			} else {
				agghkeyIndex, err = c.aggRedis[i].HGet(aggtmpkey, dntkeys).Result()
				if err != nil {
					return fmt.Errorf("构建CUBE聚集时，调用redis命令HGet出错:%s", err)
				}
			}
			for x, dimKey := range aggDimKeys[i] {
				if x == c.aggTimeDimIndexs[i] {
					continue
				}
				if err := c.aggRedis[i].SAdd(aggskey+"d:"+strconv.Itoa(x)+":"+dimKey, agghkeyIndex).Err(); err != nil {
					return fmt.Errorf("构建CUBE聚集时，调用redis命令SAdd出错:%s", err)
				}
			}
			for k, v := range measures {
				if v == nil {
					continue
				}
				fv, err := strconv.ParseFloat(base.ToString(v), 64)
				if err != nil {
					return fmt.Errorf("装载Cube时出错，指标：%s对应的值：%s不是数字！", k, v)
				}
				if err := c.aggRedis[i].HIncrByFloat(agghkey+agghkeyIndex, k, fv).Err(); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

//Aggregate 给定上下文，聚集给定的度量
func (c *Cube) Aggregate(context map[string][]string, measure, aggregator string) (val *float64, err error) {
	aggIndex := c.selectAgg(context)
	isLast := false
	if aggregator == "lastdate" {
		isLast = true
	}
	if aggIndex != -1 {
		val, err = c.aggregateByAgg(aggIndex, context, isLast, measure)
	} else {
		val, err = c.aggregateByCube(context, false, measure)
	}
	if err != nil {
		return nil, err
	}
	if aggregator == "avg" && val != nil {
		days, err := c.getDayCount(context[timeDimName])
		if err != nil {
			return nil, err
		}
		nVal := *val * float64(days)
		val = &nVal
	}
	return val, nil
}
func (c *Cube) getDayCount(timeDimKeys []string) (int, error) {
	if c.setting.BuildPeriod == "month" {
		monthMap := make(map[string]int)
		for _, key := range timeDimKeys {
			var days int
			mbytes := ([]byte(key))[:6]
			if _, ok := monthMap[string(mbytes)]; ok {
				continue
			}
			year, err := strconv.Atoi(string(mbytes[:4]))
			if err != nil {
				return 0, err
			}
			month := string(mbytes[4:6])
			if month != "02" {
				if month == "04" || month == "06" || month == "09" || month == "11" {
					days = 30
				} else {
					days = 31
				}
			} else {
				if ((year%4) == 0 && (year%100) != 0) || (year%400) == 0 {
					days = 29
				} else {
					days = 28
				}
			}
			monthMap[string(mbytes)] = days
		}
		var allDays int
		for _, v := range monthMap {
			allDays += v
		}
	}
	return len(timeDimKeys), nil
}
func (c *Cube) getTimeMap(timeDimKeys []string) map[string][]string {
	timeMap := make(map[string][]string)
	for _, timeDimKey := range timeDimKeys {
		timeBytes := []byte(timeDimKey)
		month := string(timeBytes[:6])
		day := string(timeBytes[6:])
		if _, ok := timeMap[month]; ok {
			timeMap[month] = append(timeMap[month], day)
		} else {
			days := []string{day}
			timeMap[month] = days
		}
	}
	return timeMap
}
func (c *Cube) aggregateByCube(context map[string][]string, isLastdate bool, measure string) (val *float64, err error) {
	timeDimKeys := context[timeDimName]
	inds := make(map[string]int)
	for dimName := range context {
		if dimName == timeDimName {
			continue
		}
		inds[dimName] = 0
	}
	if isLastdate {
		timeDimKeys = timeDimKeys[len(timeDimKeys)-1:]
	}
	timeMap := c.getTimeMap(timeDimKeys)
	for {
		finish := true
		for mkey, days := range timeMap {
			keyMap := make(map[string]string)
			for k, v := range context {
				if k == timeDimName {
					continue
				}
				keyMap[k] = v[inds[k]]
			}
			err = c.aggregateKeyMap(val, mkey, days, keyMap, measure)
			if err != nil {
				return nil, err
			}
		}
		for k, ind := range inds {
			if ind < len(context[k])-1 {
				inds[k] = ind + 1
				finish = false
				break
			}
		}
		if finish {
			break
		}
	}
	return
}
func (c *Cube) aggregateKeyMap(val *float64, mkey string, days []string, keyMap map[string]string, measure string) error {
	skeys := make([]string, 0)
	for k, v := range keyMap {
		if k == timeDimName {
			continue
		}
		if v == "" || v == "0" {
			continue
		}
		for _, dimU := range c.setting.DimensionUsages {
			if dimU.Name == k {
				if dimU.Relation != "" {
					skey := getRelationSetKey(dimU.Relation, mkey, v)
					skeys = append(skeys, skey)
				} else if dimU.Division != "" {
					divi, err := NewDivision(dimU.Division)
					if err != nil {
						return err
					}
					skey := divi.getRelationKey(mkey, v)
					skeys = append(skeys, skey)
				}
				break
			}
		}
	}
	if len(skeys) == 0 {
		return nil
	}
	keys, err := c.redis.SInter(skeys...).Result()
	if err != nil {
		return err
	}
	for _, key := range keys {
		err = c.addUp(val, c.hkey+mkey+":"+key, measure, days)
		if err != nil {
			return err
		}
	}
	return nil
}
func (c *Cube) aggregateByAgg(aggIndex int, context map[string][]string, isLastdate bool, measure string) (*float64, error) {
	agg := c.setting.Aggregates[aggIndex]
	dimLen := len(agg.Dimensions)
	inds := make([]int, dimLen-1, dimLen-1)
	dimsKeys := make([][]string, dimLen-1, dimLen-1)
	var timeDimKeys []string
	timeDimIndex := c.aggTimeDimIndexs[aggIndex]
	var retVal *float64
	i := 0
	var err error
	for j, dim := range agg.Dimensions {
		keys, ok := context[dim]
		if !ok || keys == nil {
			dimsKeys[i], err = dimsMap[dim].getAllLeafKeys()
			if err != nil {
				return nil, err
			}
		} else {
			dimsKeys[i] = keys
		}
		if j == timeDimIndex {
			timeDimKeys = dimsKeys[i]
		} else {
			i++
		}
	}
	if isLastdate {
		timeDimKeys = timeDimKeys[len(timeDimKeys)-1:]
	}
	timeMap := c.getTimeMap(timeDimKeys)
	for {
		finish := true
		for mKey, days := range timeMap {
			var buff bytes.Buffer
			buff.WriteString(c.agghKeys[aggIndex])
			buff.WriteString(mKey)
			buff.WriteString(":")
			for x, dimKeys := range dimsKeys {
				buff.WriteString(dimKeys[inds[x]])
				buff.WriteString("-")
			}
			buff.Truncate(buff.Len() - 1)
			c.addUp(retVal, buff.String(), measure, days)
		}
		for k, ind := range inds {
			if ind < len(dimsKeys[k])-1 {
				inds[k] = ind + 1
				finish = false
				break
			}
		}
		if finish {
			break
		}
	}
	return retVal, nil
}
func (c *Cube) indexClassify(measures []string) ([]string, []string, []string, error) {
	tpMeas := make([]string, 0)
	rgMeas := make([]string, 0)
	avgMeas := make([]string, 0)
	for _, meaName := range measures {
		find := false
		for _, mea := range c.setting.Measures {
			if meaName == mea.Name {
				if mea.Aggregator == "lastchild" {
					tpMeas = append(tpMeas, meaName)
				} else {
					rgMeas = append(rgMeas, meaName)
					if strings.ToLower(mea.Aggregator) == "avg" {
						avgMeas = append(avgMeas, meaName)
					}
				}
				find = true
			}
		}
		if !find {
			return nil, nil, nil, fmt.Errorf("给定的指标：%s在CUBE：%s中不存在！", meaName, c.setting.Name)
		}
	}
	return tpMeas, rgMeas, avgMeas, nil
}
func (c *Cube) avgMeasures(mapVals map[string]*float64, avgIndexs []string, timeDimCount int) map[string]*float64 {
	for _, avgInd := range avgIndexs {
		if mapVals[avgInd] != nil {
			avgVal := *mapVals[avgInd] / float64(timeDimCount)
			mapVals[avgInd] = &avgVal
		}
	}
	return mapVals
}
func (c *Cube) addUp(val *float64, key string, measure string, days []string) (err error) {
	var vals []interface{}
	if c.setting.BuildPeriod == "day" {
		var index int
		for i, mea := range c.setting.Measures {
			if mea.Name == measure {
				index = i
			}
		}
		vals, err = c.redis.HMGet(key+":"+strconv.Itoa(index), days...).Result()
	} else {
		var valStr string
		valStr, err = c.redis.HGet(key, measure).Result()
		vals = []interface{}{valStr}
	}
	if err != nil {
		return fmt.Errorf("执行addUp时，调用redis命令HMGet出错:%s", err)
	}
	if vals != nil {
		for _, val2 := range vals {
			if val2 == nil {
				continue
			}
			fVal, err := strconv.ParseFloat(base.ToString(val2), 64)
			if err != nil {
				return fmt.Errorf("指标：%s,值：%s不是数字！", measure, val2)
			}
			newVal := *val + fVal
			val = &newVal
		}
	}
	return nil
}

//Delete 从CUBE缓存中删除某个时点的数据
func (c *Cube) delete(date *time.Time) error {
	month := date.Format("200601")
	lkey := c.lkey + month
	hkey := c.hkey + month + ":"
	skey := c.skey + month + ":"
	if err := deleteByList(c.redis, hkey, lkey); err != nil {
		return err
	}
	if err := deleteByList(c.redis, skey, lkey); err != nil {
		return err
	}
	if err := c.redis.Del(lkey).Err(); err != nil {
		return err
	}
	for i := range c.setting.Aggregates {
		agglkey := c.agglKeys[i] + month
		agghkey := c.agghKeys[i] + month + ":"
		if err := deleteByList(c.aggRedis[i], agghkey, agglkey); err != nil {
			return err
		}
		if err := c.aggRedis[i].Del(agglkey).Err(); err != nil {
			return err
		}
	}
	return nil
}
func (c *Cube) selectAgg(context map[string][]string) int {
	for i, agg := range c.setting.Aggregates {
		isOk := true
		for k := range context {
			isFind := false
			for _, dim := range agg.Dimensions {
				if k == dim {
					isFind = true
				}
			}
			if !isFind {
				isOk = false
				break
			}
		}
		if isOk {
			return i
		}
	}
	return -1
}

var cubeMap map[string]*Cube

//GetCubeObject 获取CUBE对象
func GetCubeObject(name string) *Cube {
	return cubeMap[name]
}
